﻿using System;

namespace game_2048
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			Console.WriteLine ("2048! v1.1.0 。 this exe's author:LeoChen 。 2048's original author:Gabriele Cirulli ");

			/*
			 * 下面行注释的代码功能是输入五行五列的数字去存入5*5的game数组,刚开始写上下左右移动合并(即矩阵往一个方向合靠)功能时为方便测试用的
			 */
//			string str1,str2 = "";
//			int  j, k;
//			int[,] game = new int[5,5];
//			Console.WriteLine("请输入五行数据,每行五个数据,以空格隔开,每行输入完之后回车(输入格式不对会出错):");
//
//			for (int i = 0; i < 5; i++)
//			{
//				str1 = Console.ReadLine();
//				j = 0;
//				k = 0;
//				while (j < str1.Length)
//				{
//					if (str1[j] != ' ')
//						str2 += str1[j];
//					else
//					{
//						game[i,k++] = int.Parse(str2);	//碰到空格就将str2收集到的数字(字符型)存进数组,然后str2置空
//						str2 = "";
//					}
//					j++;
//				}
//				game[i,k] = int.Parse(str2);
//				str2 = "";
//			}

			/*
			 * 游戏初始化
			 */
			int[,] game = new int[4,4];
			int max = info (game)[0];
			int zeroCount = info (game) [1];
			int times = 0;
			Random rd = new Random ();
			zeroUpdate (game, rd, zeroCount, 2);
			zeroUpdate (game, rd, zeroCount, twoORfour (rd));
			print (game, max, times++);

			/*
			 * 游戏主循环
			 */
			while (!gameOver (game,max,zeroCount)) {
			
				Console.WriteLine ("请输入矩阵合并的方向\n（'a'代表左, 'd'代表右, 'w'代表上, 's'代表下）");
				inputAndHandle(game);

				max = info (game)[0];
				zeroCount = info (game) [1];
				zeroUpdate (game, rd, zeroCount--, twoORfour (rd));

				print (game, max, times++);

			}

			//gameOver后输出游戏结果
			if (max == 2048) {
				Console.WriteLine ("-----------------游戏结束------------------");
				Console.WriteLine ("====恭喜你成功了,操作次数{0}====",--times);
			} else {
				Console.WriteLine ("-----------------游戏结束------------------");
				Console.WriteLine ("====很遗憾,你没有成功,操作次数{0}====",--times);
			}

			//询问是否重玩,不玩了就结束程序
			if (again ()) {
				Console.Clear ();
				Main (args);
			}

		}

		//获取玩家选择的方向并作处理
		public static void inputAndHandle(int[,] game){
			string c = Console.ReadLine ();
			if (c == "a") {
				for (int i = 0; i < game.GetLength (0); i++) {
					int[] thisrow = rowext (game, i);
					leftgo (thisrow);
					rowover (game, thisrow, i);
				}
			} else if (c == "d") {
				for (int i = 0; i < game.GetLength (0); i++) {
					int[] thisrow = rowext (game, i);
					rightgo (thisrow);
					rowover (game, thisrow, i);
				}
			} else if (c == "w") {
				for (int i = 0; i < game.GetLength (1); i++) {
					int[] thiscol = colext (game, i);
					leftgo (thiscol);
					colover (game, thiscol, i);
				}
			} else if (c == "s") {
				for (int i = 0; i < game.GetLength (1); i++) {
					int[] thiscol = colext (game, i);
					rightgo (thiscol);
					colover (game, thiscol, i);
				}
			} else {
				//处理玩家的错误输入
				Console.WriteLine ("输入错误,请重新输入：\n('w'代表上,'a'代表左,'s'代表下,'d'代表右)");
				inputAndHandle (game);	//重新等待和处理玩家的选择
			}
		}

		//询问是否重新开始游戏
		public static bool again(){
			Console.Write ("重新开始?(y/n)\t");
			string c = Console.ReadLine ();
			if (c == "y")
				return true;
			if (c == "n")
				return false;
			System.Console.WriteLine ("输入错误!");
			return again ();
		}

		//抽取当前行
		public static int[] rowext(int[,] arr,int row){
			int[] temp = new int[arr.GetLength(1)];
			for (int col = 0; col < arr.GetLength (1); col++)
				temp [col] = arr [row, col];
			return temp;
			}

		//当前行覆盖式赋回
		public static void rowover(int[,] game, int[] arr,int row){
			for (int col = 0; col < arr.Length; col++) {
				game [row, col] = arr [col];
			}
		}

		//抽取当前列
		public static int[] colext(int[,] arr,int col){
			int[] temp = new int[arr.GetLength (0)];
			for(int row=0;row<temp.Length;row++){
				temp [row] = arr [row, col];
			}
			return temp;
		}

		//当前列覆盖式赋回
		public static void colover(int[,] game,int[] arr,int col){
			for (int row = 0; row < arr.Length; row++)
				game [row, col] = arr [row];
		}

		//移动数组中的0到末尾(数组非0元素左靠)
		public static void rowzero(int[] arr){
			int[] temp = new int[arr.Length];
			int t=0;
			for(int i=0;i<arr.Length;i++){
				if (arr [i] != 0)
					temp [t++] = arr [i];
			}
//			for (; t < temp.Length; t++)
//				temp [t] = 0;
			for(int i=0;i<temp.Length;i++){
				arr[i]=temp[i];
			}
		}

		//已左靠的数组元素向左同值合并后再次左靠
		public static void leftmerge(int[] arr){
			for (int i = 0; i < arr.Length-1; i++) {
				if (arr [i] == arr [i + 1]) {
					arr [i] += arr [i + 1];
					arr [i + 1] = 0;
				}
			}
			rowzero (arr);		//左靠
		}

		//数组元素倒置
		public static void invert(int[] arr){
			int low = 0, high = arr.Length - 1;
			while (low < high) {
				arr [low] = arr [low] ^ arr [high];
				arr [high] = arr [low] ^ arr [high];
				arr [low] = arr [low] ^ arr [high];
				low++;
				high--;
			}
		}

		//数组元素向左移动合并(左靠)
		public static void leftgo(int[] arr){
			rowzero (arr);
			leftmerge (arr);
		}

		//数组元素向右移动合并(右靠)
		public static void rightgo(int[] arr){
			invert (arr);
			leftgo (arr);
			invert (arr);
		}

		//获取game数组的最大值和0的数量
		public static int[] info(int[,] game){
			int[] info=new int[2];		
			int max = game [0, 0];
			int zeroCount = 0;
				foreach (int num in game){
				if (num == 0)
					zeroCount++;
				else if (num > max)
						max = num;
				}
			info [0] = max;			//info[0]存最大值
			info [1] = zeroCount;	//info[1]存0的数量值
			return info;
		}

		//输出
		public static void print(int[,] game,int max,int times)
		{
			int maxlength = max.ToString ().Length;
			if(times>0)
				Console.WriteLine ("------------------------{0}------------------------",times);
			else
				Console.WriteLine ("---------------------游戏开始----------------------");
			for (int row = 0; row < game.GetLength (0); row++) {
				for (int col = 0; col < game.GetLength (1); col++) {
					int len = game [row, col].ToString ().Length;
					if(len<maxlength)
						for(int i=0;i<maxlength-len;i++)
							System.Console.Write (" ");
					Console.Write (game[row,col]+" ");
				}
				Console.WriteLine ();
			}
		}

		/*
		 * 下面写在若干的零之中选择一个赋予2或4的功能块
		 */
		public static void zeroUpdate(int[,] arr,Random rd,int zeroCount,int twoorfour){
			if (zeroCount == 0)		//没0时zeroUpdate没意义,而且会导致程序出现error,所以直接return
				return; 
			int[,] zeroNode = zeroSearch (arr,zeroCount);
			int[] luckNode = selLuck (zeroNode,rd,zeroCount);
			arr [luckNode [0], luckNode [1]] = twoorfour;
		}

		//收集game中0元素的行列坐标
		public static int[,] zeroSearch(int[,] arr,int zeroCount){
			int[,] zeroNode = new int[zeroCount, 2];
			//Console.WriteLine ("------------{0}--------------",zeroCount);
			int r = 0;
			for (int row=0; row < arr.GetLength (0); row++) {
				for (int col = 0; col < arr.GetLength (1); col++) {
					if (arr [row, col] == 0) {
						zeroNode [r, 0] = row;
						zeroNode [r++, 1] = col;
//						if (r < zeroCount - 1)
//							r++;
					}
				}	
			}
			return zeroNode;
		}

		//在收集的0元素行列坐标中抽取一个,将被赋予2或4
		public static int[] selLuck(int[,] zeroNode,Random rd,int zeroCount){
			int luckone = rd.Next (0, zeroCount);
			int[] luckNode = new int[2]{ zeroNode [luckone, 0], zeroNode [luckone, 1] };
			return luckNode;
		}

		//被抽中的幸运0元素被赋予2或4的概率比例为4:1
		public static int twoORfour(Random rd){
			int num = rd.Next (1, 101);
			if (num <= 80)
				return 2;
			else
				return 4;
		}


		//下面是判断游戏是否结束的函数
		public static bool gameOver(int[,] game,int max,int zeroCount){
			if (max == 2048)
				return true;
			else if (zeroCount == 0) {
				if (canContinue (game))
					return false;
				else
					return true;
			} else
				return false;
		}

		public static bool canContinue(int[,] game){
			bool flag = false;
			for (int row = 0; row < game.GetLength (0); row++) {
				for (int col = 0; col < game.GetLength (1) - 1; col++) {
					if (game [row, col] == game [row, col + 1]) {
						flag = true;
						break;
					}
					if (flag)
						break;
				}
			}
			if (flag)
				return flag;
			for (int col = 0; col < game.GetLength (1); col++) {
				for (int row = 0; row < game.GetLength (0) - 1; row++) {
					if (game [row, col] == game [row+1, col]) {
						flag = true;
						break;
					}
					if (flag)
						break;
				}
			}
			return flag;
		}
			
	}
}
